package org.groovyflow.interceptor;

/**
 * Supports Rails-style filters, with one modification, as they appear in
 * http://www.therailsway.com/2006/11/16/tracks-part-2
 * TODO  Document this.
 */

class RailsStyleInterceptorLogic implements InterceptorLogic{
	
	public static final String EXCEPT = "except"
    public static final String ONLY = "only"
    def filterProperty //Name of the property that holds the filter information
    def skipFilterProperty     
    def obj
    def methName

    def filters  //intermediate values
    def skipThese

    def filtersToInvoke = []//result
    
    
    public RailsStyleInterceptorLogic(){}
    public RailsStyleInterceptorLogic(filterProperty, skipFilterProperty){
		this.filterProperty = filterProperty
		this.skipFilterProperty = skipFilterProperty
	}

    def clearIntermediate(){
        filters = null
        skipThese = null
        filtersToInvoke = []
    }

    public void clearObject(){
      clearIntermediate()
      obj = null
      methName = null
    }

    def clear(){
     clearObject()
     filterProperty = null
     skipFilterProperty = null
     
  }

   public void findFilters(){
         if(! hasProperty(filterProperty))
            return
         filters = obj."$filterProperty"
         iter()
    }

   def iter(){
       for(filter in filters){
             println "filter is " + filter
             if(! (filter instanceof String)) throw new RuntimeException("Filter List must be made up entirely of Strings")
             String filterInfoName = filter + filterProperty[0].toUpperCase() + filterProperty.substring(1)
             println "filterInfoName is " + filterInfoName
             def filterInfo = getPropertyOfObj(filterInfoName)
            
             println "filterInfo is " + filterInfo     
             if(filterInfo instanceof Map){
                 boolean ok = handleMapFilterInfo(filterInfo)
                 if(!ok)
                     continue;
             }   

             if(filterInfo instanceof List){ 
                 if(! filterInfo.any{it == methName} )
                    continue;
             }       

             if(!shouldSkip(filter))
                filtersToInvoke << filter
             
       }
   }
  
   def handleMapFilterInfo(map){
        if(map.size() != 1) throw new RuntimeException("Map must be of size 1")
        def foundMethodNameInList
        def exceptOrOnly 
        boolean tf = map.each{key, value ->
            exceptOrOnly = key
            foundMethodNameInList = value.any{it == methName}
        }
        if(exceptOrOnly == EXCEPT)
             return !foundMethodNameInList
        else if(exceptOrOnly == ONLY)
             return foundMethodNameInList
        else 
             throw new RuntimeException("Map key must be either $EXCEPT or $ONLY.  Value of $exceptOrOnly is not permitted.")
   }
   def hasProperty(propName) {
         obj.metaClass.hasProperty(obj, propName) != null
   }

   def getPropertyOfObj(propName){
      return hasProperty(propName) ? obj."$propName" : null
   }

   def shouldSkip(filter){
       if(skipThese == "NONE") return false
       skipThese =  hasProperty("$skipFilterProperty") ? obj."$skipFilterProperty" : "NONE"
       if(skipThese == "NONE") return false	
       if(skipThese instanceof String)
    	   return skipThese == filter
       else
           return skipThese.any{ it == filter}
   }	
}